#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
根据 warnings 计算任务危险等级分数
"""

from app.libs.enums import ChangeCheckType


class Score(object):
    severityMap = {
        0: {"key": 'UNKNOWN', "zh_CN": '低危', "level": 2, "front_CN": "低危", "score_range": (-1, 0)},
        1: {"key": 'INFO', "zh_CN": '低危', "level": 2, "front_CN": "低危", "score_range": (-1, 0)},
        2: {"key": 'LOW', "zh_CN": '低危', "level": 2, "front_CN": "低危", "score_range": (1, 25)},
        3: {"key": 'MEDIUM', "zh_CN": '中危', "level": 3, "front_CN": "中危", "score_range": (26, 50)},
        4: {"key": 'HIGH', "zh_CN": '高危', "level": 4, "front_CN": "高危", "score_range": (51, 75)},
        5: {"key": 'CRITICAL', "zh_CN": '严重', "level": 5, "front_CN": "严重", "score_range": (76, 100)},
    }
    severityKnownLevelNum = [1, 2, 3, 4, 5]
    severityAllLevelNum = [0, 1, 2, 3, 4, 5]

    @staticmethod
    def max_score(num, max_num):
        if num > max_num:
            return max_num
        return num

    @staticmethod
    def _score(event_map, warnings):
        sec_keys = ["low", "low", "low", "medium", "high", "critical"]
        event_map.update({"info": 0, "low": 0, "medium": 0, "high": 0, "critical": 0, "score": 0, "count": 0})
        for warning in warnings:
            if isinstance(warning, dict):
                event_map[sec_keys[int(warning.get('level'))]] += 1
            else:
                event_map[sec_keys[warning.level]] += 1
            event_map["count"] += 1
        if event_map.get("critical"):
            event_map["score"] = Score.max_score(75 + event_map.get("critical"), 100)
            return event_map
        if event_map.get("high"):
            event_map["score"] = Score.max_score(50 + event_map.get("high"), 75)
            return event_map
        if event_map.get("medium"):
            event_map["score"] = Score.max_score(25 + event_map.get("medium"), 50)
            return event_map
        if event_map.get("low"):
            event_map["score"] = Score.max_score(0 + event_map.get("low"), 25)
            return event_map
        return event_map

    @staticmethod
    def _parse_vul_warnings(warnings, enable=False):
        vul_map = {"enable": enable}
        Score._score(vul_map, warnings)
        return vul_map

    @staticmethod
    def _parse_ssl_warnings(warnings, enable=False):
        ssl_map = {"certificate": 0, "protocol": 0, "enable": enable}
        for warning in warnings:
            if isinstance(warning, dict):
                category = warning.get("category")
            else:
                category = warning.category
            if category in ssl_map:
                ssl_map[category] += 1
        Score._score(ssl_map, warnings)
        return ssl_map

    @staticmethod
    def _parse_security_event_warnings(warnings, enable=False):
        security_event_map = Score.__parse_security_event_warnings(warnings, enable, only_parse=True)
        return security_event_map

    @staticmethod
    def get_security_event_severity(warnings):
        security_event_severity_map = Score.__parse_security_event_warnings(warnings, False, only_parse=False)
        return security_event_severity_map

    @staticmethod
    def __parse_security_event_warnings(warnings, enable=False, only_parse=True) -> dict:
        security_event_map = {
            "black_links": 0,
            "broken_links": 0,
            "foreign_links": 0,
            "cryjack": 0,
            "malscan": 0,
            "enable": enable
        }
        security_event_severity_map = {
            "black_links": 0,
            "broken_links": 0,
            "foreign_links": 0,
            "cryjack": 0,
            "malscan": 0,
        }
        for warning in warnings:
            if isinstance(warning, dict):
                category = warning.get("category")
            else:
                category = warning.category

            if category not in security_event_severity_map:
                continue
            if only_parse:
                security_event_map[category] += 1
            else:
                security_event_severity_map[category] = max(
                    security_event_severity_map[category],
                    warning.level
                )
        if only_parse:
            Score._score(security_event_map, warnings)
            return security_event_map
        else:
            return security_event_severity_map

    @staticmethod
    def _parse_content_warnings(warnings, enable=False):
        content_map = {"keyword": 0, "privacy_disclosure": 0, "enable": enable}
        for warning in warnings:
            if isinstance(warning, dict):
                category = warning.get("category")
            else:
                category = warning.category
            if category in content_map:
                content_map[category] += 1
        Score._score(content_map, warnings)
        # 内容违规监测所有都是低危
        for k in ["info", "medium", "high", "critical"]:
            content_map["low"] += content_map[k]
            content_map[k] = 0
        content_map["score"] = Score.max_score(content_map["low"], 25)
        return content_map

    @staticmethod
    def _parse_change_check_warnings(warnings, enable=False):
        type_count = {t.value: 0 for t in ChangeCheckType}
        for warning in warnings:
            if isinstance(warning, dict):
                detail = warning.get("detail")
            else:
                detail = warning.detail
            for t in ChangeCheckType:
                if detail.get(t.value):
                    type_count[t.value] += 1
        change_check_map = {"enable": enable, **type_count}
        Score._score(change_check_map, warnings)
        return change_check_map

    @staticmethod
    def _score_level(score):
        if score == 0:
            return "安全"
        if 0 < score <= 25:
            return "低危"
        elif 25 < score <= 50:
            return "中危"
        elif 50 < score <= 75:
            return "高危"
        elif 75 < score <= 100:
            return "严重"
        else:
            return "未知"

    @staticmethod
    def score_info(event_map, warnings):
        score_info = Score._score(event_map, warnings)
        score_info['level'] = Score._score_level(score_info.get('score', 0))
        return score_info

    @staticmethod
    def check_level_num(num, is_all=False):
        try:
            num = int(num)
        except (ValueError, TypeError):
            return False
        if is_all:
            return num in Score.severityAllLevelNum
        return num in Score.severityKnownLevelNum
